import * as step from '@flow-step/step-toolkit'
import * as sinon from 'sinon'
import {expect} from 'chai'
import proxyquire from 'proxyquire'
import {IParams} from '../src/params'

// Mock dependencies
const mockInstall = {
    installKubectl: sinon.stub()
}

const mockCertificate = {
    getKubernetesCertificate: sinon.stub()
}

const mockUtil = {
    hasFilesInDirectory: sinon.stub()
}

const mockI18n = {
    polyglot: {
        t: sinon.stub()
    }
}

// Mock fs module
const mockFs = {
    mkdirSync: sinon.stub(),
    writeFileSync: sinon.stub(),
    existsSync: sinon.stub(),
    copyFileSync: sinon.stub(),
    statSync: sinon.stub(),
    chmodSync: sinon.stub(),
    rmSync: sinon.stub(),
    unlinkSync: sinon.stub()
}

// Import the execute function with mocked dependencies
const {execute} = proxyquire('../src/execute', {
    './install': mockInstall,
    './certificate': mockCertificate,
    './util': mockUtil,
    './i18n': mockI18n,
    'fs': mockFs
})

describe('Execute Function', () => {
    let sandbox: sinon.SinonSandbox
    let mockParams: IParams
    let stepStubs: any

    beforeEach(() => {
        sandbox = sinon.createSandbox()

        // Mock step-toolkit methods
        stepStubs = {
            infoV1: sandbox.stub(step, 'infoV1'),
            debug: sandbox.stub(step, 'debug'),
            warnV1: sandbox.stub(step, 'warnV1'),
            exec: {
                call: sandbox.stub(step.exec, 'call')
            },
            addOutput: sandbox.stub(step, 'addOutput')
        }

        // Setup default mocks
        mockInstall.installKubectl.resolves('/mock/path')
        mockUtil.hasFilesInDirectory.resolves(true)
        mockI18n.polyglot.t.callsFake((key: string) => key)

        // Create mock parameters
        mockParams = {
            kubectlVersion: 'v1.18.4',
            clusterType: 'ack',
            namespace: 'default',
            pathToYaml: 'manifests',
            insecureSkipTlsVerify: false,
            projectDir: '/mock/project',
            workSpace: '/mock/workspace',
            images: 'image1:tag1,image2:tag2',
            skipUndefineVariable: false,
            useReplace: false,
            debug: false
        } as IParams

        // Mock file system operations
        const mockStats = {
            mode: 0o755
        }
        mockFs.statSync.returns(mockStats)
        mockFs.existsSync.returns(false)

        // Reset all stubs
        sandbox.resetHistory()
        mockInstall.installKubectl.resetHistory()
        mockCertificate.getKubernetesCertificate.resetHistory()
        mockUtil.hasFilesInDirectory.resetHistory()
        mockI18n.polyglot.t.resetHistory()
        
        // Reset mocked fs methods
        mockFs.mkdirSync.resetHistory()
        mockFs.writeFileSync.resetHistory()
        mockFs.existsSync.resetHistory()
        mockFs.copyFileSync.resetHistory()
        mockFs.statSync.resetHistory()
        mockFs.chmodSync.resetHistory()
        mockFs.rmSync.resetHistory()
        mockFs.unlinkSync.resetHistory()
    })

    afterEach(() => {
        sandbox.restore()
    })

    describe('Custom Cluster Type', () => {
        it('should handle custom cluster type with valid kubeConfig', async () => {
            // Arrange
            const mockCert = {
                type: 'custom',
                kubeConfig: 'mock-kube-config',
                miranaProxy: '',
                jwtToken: ''
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)
            mockParams.clusterType = 'custom'

            // Act
            await execute(mockParams)

            // Assert
            sinon.assert.calledOnceWithExactly(mockInstall.installKubectl, 'v1.18.4')
            sinon.assert.calledWith(mockFs.writeFileSync, sinon.match.string, 'mock-kube-config')
            sinon.assert.called(stepStubs.exec.call)
            sinon.assert.calledWith(stepStubs.addOutput, 'RERUNABLE=true')
        })

        it('should throw error when kubeConfig is empty for custom cluster', async () => {
            // Arrange
            const mockCert = {
                type: 'custom',
                kubeConfig: '',
                miranaProxy: '',
                jwtToken: ''
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            mockParams.clusterType = 'custom'

            // Act & Assert
            try {
                await execute(mockParams)
                expect.fail('Expected an error to be thrown')
            } catch (error) {
                sinon.assert.calledWith(mockI18n.polyglot.t, 'failed_to_obtain_kubernetes_certificate_file')
            }
        })
    })

    describe('ACK Cluster Type with miranaProxy Check', () => {
        it('should handle ACK cluster with valid miranaProxy', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: '',
                miranaProxy: 'https://mock-proxy.com',
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)

            // Act
            await execute(mockParams)

            // Assert
            sinon.assert.calledWith(mockFs.writeFileSync, sinon.match.string, '')
            sinon.assert.called(stepStubs.exec.call)
            sinon.assert.calledWith(stepStubs.addOutput, 'RERUNABLE=true')
        })

        it('should fall back to custom logic when miranaProxy is empty', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: 'fallback-kube-config',
                miranaProxy: '', // Empty miranaProxy
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)

            // Act
            await execute(mockParams)

            // Assert
            sinon.assert.calledWith(mockFs.writeFileSync, sinon.match.string, 'fallback-kube-config')
            sinon.assert.called(stepStubs.exec.call)
        })

        it('should fall back to custom logic when miranaProxy is undefined', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: 'fallback-kube-config',
                miranaProxy: undefined, // Undefined miranaProxy
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)

            // Act
            await execute(mockParams)

            // Assert
            sinon.assert.calledWith(mockFs.writeFileSync, sinon.match.string, 'fallback-kube-config')
        })
    })

    describe('Error Handling', () => {
        it('should throw error when kubectl installation fails', async () => {
            // Arrange
            mockInstall.installKubectl.rejects(new Error('Installation failed'))

            // Act & Assert
            try {
                await execute(mockParams)
                expect.fail('Expected an error to be thrown')
            } catch (error) {
                expect((error as Error).message).to.contain('failed to install kubectl but err')
            }
        })

        it('should throw error when no files found in manifest directory', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: '',
                miranaProxy: 'https://mock-proxy.com',
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)
            mockUtil.hasFilesInDirectory.resolves(false) // No files found

            // Act & Assert
            try {
                await execute(mockParams)
                expect.fail('Expected an error to be thrown')
            } catch (error) {
                sinon.assert.calledWith(mockI18n.polyglot.t, 'no_published_resources_found')
            }
        })
    })

    describe('kubectl Version Handling', () => {
        it('should use --dry-run=client for kubectl v1.18.4', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: '',
                miranaProxy: 'https://mock-proxy.com',
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)
            mockParams.kubectlVersion = 'v1.18.4'

            // Act
            await execute(mockParams)

            // Assert - Check if any call contains --dry-run=client
            const dryRunCall = stepStubs.exec.call.getCalls().find((call: any) => 
                call.args[1] && call.args[1].includes('--dry-run=client')
            )
            expect(dryRunCall).to.exist
        })
    })

    describe('File Operations', () => {
        it('should create and clean up temporary directories', async () => {
            // Arrange
            const mockCert = {
                type: 'ack',
                kubeConfig: '',
                miranaProxy: 'https://mock-proxy.com',
                jwtToken: 'mock-jwt-token'
            }
            mockCertificate.getKubernetesCertificate.resolves(mockCert)
            stepStubs.exec.call.resolves(0)

            // Act
            await execute(mockParams)

            // Assert
            sinon.assert.called(mockFs.mkdirSync)
            sinon.assert.called(mockFs.rmSync)
        })
    })
})